﻿#region Copyright

// // ======================================================
// //     Copyright (c) 2011 Toulr All rights reserved.
// //    
// //     The use and distribution terms for this software are contained in the file
// //     named license.txt, which can be found in the root of this distribution.
// //     By using this software in any fashion, you are agreeing to be bound by the
// //     terms of this license.
// //    
// //     You must not remove this notice, or any other, from this software.
// // ======================================================

#endregion

#region Using Namespaces

using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.Drawing;
using System.Net.Mime;
using System.Reflection;
using System.Resources;

#endregion

namespace Toulr.Util.Resource
{
    /// <summary>
    ///   封装查找资源的通用流程.
    ///   对窗体/控件, 可直接从嵌入的资源中获取对象;
    ///   对非窗体的类, 在项目中查找该类名称后加上Resources.resx的资源;
    ///   也可从指定的<see cref = "ResourceManager" />中获取资源,
    ///   利用本类提供的获取<see cref = "Bitmap" />和<see cref = "MediaTypeNames.Image" />对象的封装方法.
    /// </summary>
    public class ResReader
    {
        /// <summary>
        ///   非窗体类对应的资源文件，命名约定为[类名].Resources.resx
        /// </summary>
        public const string ResourceFileNameSuffix = "Resources";

        /// <summary>
        ///   程序集默认资源文件的特征名称
        /// </summary>
        public const string DefaultResourcePattern = "Properties.Resources";

        /// <summary>
        ///   二进制资源文件或嵌入资源的扩展名
        /// </summary>
        public const string ResourceFileExtension = "resources";

        /// <summary>
        ///   缓存 assembly => resourceNameSet 的对应关系
        /// </summary>
        private static IDictionary<Assembly, IDictionary<string, string>> _asmResRepo;

        /// <summary>
        ///   缓存 assembly => defaultResourceBaseName 的对应关系
        /// </summary>
        private static IDictionary<Assembly, string> _defaultResources;

        private static ResReader _default;

        protected ResReader()
        {
        }

        #region 主要接口

        #region 子类覆写

        /// <summary>
        ///   从资源中获取指定的字符串
        /// </summary>
        /// <param name = "key"></param>
        /// <returns></returns>
        public virtual string GetString(string key)
        {
            return key;
        }

        /// <summary>
        ///   从资源中获取指定的位图图像
        /// </summary>
        /// <param name = "key"></param>
        /// <returns></returns>
        public virtual object GetObject(string key)
        {
            return null;
        }

        #endregion

        /// <summary>
        ///   获取窗体、列表中使用的图标.
        /// </summary>
        /// <param name = "key"></param>
        /// <returns></returns>
        public Icon GetIcon(string key)
        {
            object o = GetObject(key);
            Icon i = (o as Icon);
            return i;
        }

        /// <summary>
        ///   获取<see cref = "MediaTypeNames.Image" />类型的图像.
        /// </summary>
        /// <param name = "key"></param>
        /// <returns></returns>
        public Image GetImage(string key)
        {
            object o = GetObject(key);
            Image i = (o as Image);
            return i;
        }

        /// <summary>
        ///   获取位图图像.
        /// </summary>
        /// <param name = "key"></param>
        /// <returns></returns>
        public Bitmap GetBitmap(string key)
        {
            object o = GetObject(key);
            Bitmap b = (o as Bitmap);
            return b;
        }

        /// <summary>
        ///   获取位图图像. 如果超过指定的大小,则予以缩小
        /// </summary>
        /// <param name = "key"></param>
        /// <param name = "maxSize"></param>
        /// <returns></returns>
        public Bitmap GetBitmap(string key, int maxSize)
        {
            Bitmap b1 = GetBitmap(key);
            if (b1 == null)
                return null;

            if (b1.Width <= maxSize && b1.Height <= maxSize)
                return b1;

            int width, height;
            if (b1.Width == b1.Height)
            {
                width = maxSize;
                height = maxSize;
            }
            else if (b1.Width > b1.Height)
            {
                width = maxSize;
                height = maxSize*b1.Height/b1.Width;
            }
            else
            {
                height = maxSize;
                width = maxSize*b1.Width/b1.Height;
            }
            Bitmap b2 = new Bitmap(b1, width, height);
            return b2;
        }

        #endregion

        #region 通过ResourceManager获取资源的子类

        /// <summary>
        /// </summary>
        public class ResManagerReader : ResReader
        {
            private readonly ResourceManager _res;

            public ResManagerReader(ResourceManager resourceManager)
            {
                _res = resourceManager;
            }

            public override string GetString(string key)
            {
                string result = _res.GetString(key);
                return result;
            }

            public override object GetObject(string key)
            {
                object o = _res.GetObject(key);
                return o;
            }
        }

        #endregion

        private static IDictionary<Assembly, IDictionary<string, string>> ResourceDirectories
        {
            get
            {
                if (_asmResRepo == null)
                    _asmResRepo = new Dictionary<Assembly, IDictionary<string, string>>();
                return _asmResRepo;
            }
        }

        private static IDictionary<Assembly, string> DefaultResourceNames
        {
            get
            {
                if (_defaultResources == null)
                    _defaultResources = new Dictionary<Assembly, string>();
                return _defaultResources;
            }
        }

        public static ResReader Default
        {
            get
            {
                if (_default == null)
                    _default =
                        new ResManagerReader(new ResourceManager("Common.Resource.Default", typeof (ResReader).Assembly));
                return _default;
            }
        }

        /// <summary>
        ///   查阅与指定程序集相关的资源列表.
        /// </summary>
        /// <param name = "assembly"></param>
        /// <returns></returns>
        protected static IDictionary<string, string> GetResourceNameDirectory(
            Assembly assembly)
        {
            if (!ResourceDirectories.ContainsKey(assembly))
                BuildResourceNameDirectory(assembly);

            IDictionary<string, string> result = _asmResRepo[assembly];
            return result;
        }

        /// <summary>
        ///   返回与指定程序集对应的默认资源文件名称.
        /// </summary>
        /// <param name = "assembly"></param>
        /// <returns></returns>
        protected static string GetDefaultResourceBaseName(Assembly assembly)
        {
            if (!DefaultResourceNames.ContainsKey(assembly))
                BuildResourceNameDirectory(assembly);

            string result = DefaultResourceNames[assembly];
            result = result.Substring(0, result.LastIndexOf("."));
            return result;
        }

        /// <summary>
        ///   向 ResourceDirectories 和 DefaultResourceNames 中添加
        /// </summary>
        /// <param name = "assembly"></param>
        private static void BuildResourceNameDirectory(Assembly assembly)
        {
            IDictionary<string, string> directory =
                new Dictionary<string, string>();
            ResourceDirectories[assembly] = directory;
            DefaultResourceNames[assembly] = null;

            string[] resNames = assembly.GetManifestResourceNames();
            foreach (string resName in resNames)
            {
                directory[resName] = resName;

                if (resName.Contains(DefaultResourcePattern))
                    DefaultResourceNames[assembly] = resName;
            }
        }

        private static string JoinParts(params string[] parts)
        {
            return string.Join(".", parts);
        }

        //private static RR _readerForFiles;

        ///// <summary>
        ///// 访问资源文件目录内的图片文件等资源
        ///// </summary>
        //public static RR ForFiles
        //{
        //    get
        //    {
        //        if (_readerForFiles == null)
        //            _readerForFiles = new FilesReader();
        //        return _readerForFiles;
        //    }
        //}

        /// <summary>
        ///   根据调用方所在的类, 获取用来访问其对应资源文件的实例.
        ///   尝试顺序: 窗体/控件内嵌资源 > 类名+Resources.rex > 程序集中的
        ///   Properties\Resources.resx
        /// </summary>
        /// <returns></returns>
        public static ResReader Get()
        {
            Type callingType = new StackFrame(1, false).GetMethod().DeclaringType;
            IDictionary<string, string> resDir = GetResourceNameDirectory(callingType.Assembly);

            ResourceManager manager = null;
            string fullName = callingType.FullName;
            if (resDir.ContainsKey(JoinParts(fullName, ResourceFileExtension)))
                manager = new ResourceManager(callingType);
            else
            {
                string resName2 = JoinParts(
                    fullName, ResourceFileNameSuffix);
                if (resDir.ContainsKey(JoinParts(resName2, ResourceFileExtension)))
                {
                    manager = new ResourceManager(resName2, callingType.Assembly);
                }
                else
                {
                    string defaultResource = GetDefaultResourceBaseName(callingType.Assembly);
                    if (!string.IsNullOrEmpty(defaultResource))
                    {
                        manager = new ResourceManager(defaultResource, callingType.Assembly);
                    }
                }
            }

            return manager == null ? new ResReader() : new ResManagerReader(manager);
        }

        /// <summary>
        ///   指定名称和程序集, 获取用来访问其对应资源文件的实例.
        /// </summary>
        /// <param name = "baseName"></param>
        /// <param name = "assembly"></param>
        /// <returns></returns>
        public static ResReader Get(string baseName, Assembly assembly)
        {
            StackFrame frame = new StackFrame(1, false);
            Type callingType = frame.GetMethod().DeclaringType;
            IDictionary<string, string> resDir = GetResourceNameDirectory(callingType.Assembly);

            if (!baseName.Contains("."))
                baseName = JoinParts(callingType.Namespace, baseName);

            ResourceManager manager = null;
            if (resDir.ContainsKey(JoinParts(baseName, ResourceFileExtension)))
                manager = new ResourceManager(baseName, callingType.Assembly);

            return manager == null ? new ResReader() : new ResManagerReader(manager);
        }

        /// <summary>
        ///   根据指定的类型, 获取用来访问该窗体或控件的内嵌资源的实例.
        /// </summary>
        /// <param name = "resourceSource"></param>
        /// <returns></returns>
        public static ResReader Get(Type resourceSource)
        {
            StackFrame frame = new StackFrame(1, false);
            Type callingType = frame.GetMethod().DeclaringType;
            IDictionary<string, string> resDir = GetResourceNameDirectory(callingType.Assembly);

            ResourceManager manager = null;
            if (resDir.ContainsKey(JoinParts(resourceSource.FullName, ResourceFileExtension)))
                manager = new ResourceManager(resourceSource);
            else
            {
                string baseName = JoinParts(resourceSource.FullName, ResourceFileNameSuffix);
                if (resDir.ContainsKey(JoinParts(baseName, ResourceFileExtension)))
                    manager = new ResourceManager(baseName, callingType.Assembly);
            }

            return manager == null ? new ResReader() : new ResManagerReader(manager);
        }

        /// <summary>
        ///   根据指定的<paramref name = "resourceManager" />, 获取用来访问其中资源的实例.
        /// </summary>
        /// <param name = "resourceManager"></param>
        /// <returns></returns>
        public static ResReader Get(ResourceManager resourceManager)
        {
            return new ResManagerReader(resourceManager);
        }
    }
}